package com.gilight.base.config.jwt;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gilight.base.base.JwtUserToken;
import com.gilight.base.component.CacheComponent;
import com.gilight.base.config.jwt.common.Constant;
import com.gilight.base.config.jwt.util.JwtUtil;
import com.gilight.base.config.jwt.util.common.StringUtil;
import com.gilight.base.dao.SysPermissionMapper;
import com.gilight.base.dao.SysRoleMapper;
import com.gilight.base.dao.SysUserMapper;
import com.gilight.base.dto.RoleDto;
import com.gilight.base.dto.UserDto;
import com.gilight.base.mapper.UserMapper;
import com.gilight.base.model.SysPermission;
import com.gilight.base.model.SysRole;
import com.gilight.base.model.SysUser;
import com.gilight.base.model.SysUserDTO;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 自定义Realm
 * @author dolyw.com
 * @date 2018/8/30 14:10
 */
@Service
public class UserRealm extends AuthorizingRealm {

    private final SysUserMapper userMapper;
    private final SysRoleMapper roleMapper;
    private final SysPermissionMapper permissionMapper;
    @Autowired
    private CacheComponent cacheComponent;
    @Autowired
    public UserRealm(SysUserMapper userMapper, SysRoleMapper roleMapper, SysPermissionMapper permissionMapper) {
        this.userMapper = userMapper;
        this.roleMapper = roleMapper;
        this.permissionMapper = permissionMapper;
    }

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof JwtToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        try {
            //注入角色(查询所有的角色注入控制器）
            List<SysRole> list = roleMapper.selectByUserId(sysUser.getId());
            for (SysRole role: list){
                authorizationInfo.addRole(role.getId().toString());
            }
            //注入角色所有权限（查询用户所有的权限注入控制器）
            List<SysPermission>  permissions= permissionMapper.selectByUserId(sysUser.getId());
            for(SysPermission sysPermission:permissions){
                authorizationInfo.addStringPermission(sysPermission.getId().toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return authorizationInfo;
    }

    /**
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        JwtToken jwtToken = (JwtToken)authenticationToken;
        String token = jwtToken.getToken();
        if (token != null && token.startsWith("Bearer ")) {
            token =  token.substring(7);
        }
        // 解密获得account，用于和数据库进行对比
        String account = JwtUtil.getClaim(token, Constant.ACCOUNT);

        // 帐号为空
        if (StringUtil.isBlank(account)) {
            throw new AuthenticationException("Token中帐号为空(The account in Token is empty.)");
        }
        // 查询用户是否存在
        SysUser sysUser = new SysUser();
        sysUser = JSON.parseObject(account,SysUser.class);

        sysUser = userMapper.selectByUserCode(sysUser.getUserCode());
        if (sysUser == null) {
            throw new AuthenticationException("该帐号不存在(The account does not exist.)");
        }
        JwtUserToken jwtUserToken = JSONObject.parseObject(account,JwtUserToken.class);
        if(jwtUserToken==null){
            jwtUserToken = new JwtUserToken();
        }
        // 开始认证，要AccessToken认证通过，且Redis中存在RefreshToken，且两个Token时间戳一致
        if (JwtUtil.verify(token) && cacheComponent.hasKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN + jwtUserToken.toString())) {
            // 获取RefreshToken的时间戳
            String currentTimeMillisRedis = cacheComponent.getObj(Constant.PREFIX_SHIRO_REFRESH_TOKEN + jwtUserToken.toString(),String.class);
            // 获取AccessToken时间戳，与RefreshToken的时间戳对比
            if (JwtUtil.getClaim(token, Constant.CURRENT_TIME_MILLIS).equals(currentTimeMillisRedis)) {
                return new SimpleAuthenticationInfo(token, token, "userRealm");
            }
        }
        throw new AuthenticationException("Token已过期(Token expired or incorrect.)");
    }
}
